/*
 * 	Name		: LMX2541.c
 *	Author	 	: Tigran Papazyan
 *  Created on	: Nov 18, 2010
 *  Copyright   : Copyright Antel Systems
 *  Description : LMX2541 PLL Driver
 */



#include "board.h"
#include "LMX2541.h"
#include "mac/mac.h"
#include "AntelCfgOID.h"
#include "CfgParams.h"
#include "cmx7164_api.h"
#include "gpio.h"
#include "tc_1.h"

#define LMX_VCO_LOWER_LIMIT 0x946A6280 	// 2490 Mhz
#define LMX_VCO_UPPER_LIMIT 0xAAC46E40 	// 2865 Mhz

#define LMX_FOSC  			16000000		//16 Mhz
#define LMX_OSC_FREQ		16
#define LMX_R_COUNTER		1
#define LMX_OSC_2X			1
#define LMX_PLL_N			93
#define LMX_NUM				0x2FFFFE
#define LMX_AC_TEMP_COMP	24

//#define LMX_VCO_DEN	 		0xFFFD 	// 65533
#define DOUBLER_ON			(1+LMX_OSC_2X)
#define LMX_VCO_DEN	 	0x3FFFFD

void LMX2541_Interface_Init(void)
{
	gpio_enable_gpio_pin(LMX2541_DATA_OUT_PIN);
	gpio_enable_gpio_pin(LMX2541_CLK_PIN);
	gpio_enable_gpio_pin(LMX2541_LE_PIN);

	gpio_clr_gpio_open_drain_pin(LMX2541_DATA_OUT_PIN);
	gpio_clr_gpio_open_drain_pin(LMX2541_CLK_PIN);
	gpio_clr_gpio_open_drain_pin(LMX2541_LE_PIN);

	gpio_clr_gpio_pin(LMX2541_DATA_OUT_PIN);
	gpio_clr_gpio_pin(LMX2541_CLK_PIN);
	gpio_set_gpio_pin(LMX2541_LE_PIN);

	gpio_get_pin_value(LMX2541_FTEST_PIN);
}

void LMX2541_Write(unsigned char* data ,unsigned int byte_count, unsigned int word_length_bit)
{
	int j,i ;
	unsigned int temp_data ;
	byte_count /= (word_length_bit/8);
	for(j = 0 ; j < byte_count ; j++)
	{
		gpio_clr_gpio_pin(LMX2541_LE_PIN);
		for(i = word_length_bit-1 ; i >=0 ; i-- )
		{
			temp_data = data[j*4+3-(i/8)];
			if(((unsigned char)data[j*4+3-(i/8)] >> i%8) & 0x01)
				gpio_set_gpio_pin(LMX2541_DATA_OUT_PIN);
			else
				gpio_clr_gpio_pin(LMX2541_DATA_OUT_PIN);
			gpio_set_gpio_pin(LMX2541_CLK_PIN);
			gpio_clr_gpio_pin(LMX2541_CLK_PIN);
		}
		gpio_clr_gpio_pin(LMX2541_DATA_OUT_PIN);
		gpio_set_gpio_pin(LMX2541_LE_PIN);
	}
	gpio_clr_gpio_pin(LMX2541_LE_PIN);
}

int LMX2541_In_lock(void)
{
	return gpio_get_pin_value(LMX2541_FTEST_PIN);
}

int LMX2541_Init()
{
	LMX2541_Interface_Init();

	LMX2541_Frequency = 455000000;

	LMX_REG.REG7 = 0x00000017 ; 						//Must be programmed 	-> (Reset to default values)

	LMX_REG.REG13.DWORD_REG13 = 0x00000080 ;
	LMX_REG.REG13.REG13_bits.address = 13 ;
	LMX_REG.REG13.REG13_bits.VCO_DIV_OPT = 0;

	LMX_REG.REG12 = 0x0000001C;							//Must be programmed (internal VCO mode)

	LMX_REG.REG9 = 0x28001409;

	LMX_REG.REG8.DWORD_REG8 = 0x0111CE00 ;
	LMX_REG.REG8.REG8_bits.address = 8;					//AC_TEMP_COMP[8:4]	= 5 ->	(Dynamic Frequency) , 24 - >(Fixed Frequency) 0x0111CE58
	LMX_REG.REG8.REG8_bits.AC_TEMP_COMP = 5;

	LMX_REG.REG6.DWORD_REG6 = 0x001C0000;
	LMX_REG.REG6.REG6_bits.address = 6;					// 0x001F3326
	LMX_REG.REG6.REG6_bits.RF_OUT = 2;					//RFOUT enabled
//	LMX_REG.REG6.REG6_bits.RF_OUT = 0;					//RFOUT disabled
	LMX_REG.REG6.REG6_bits.VCO_GAIN = 6;				//VCOGAIN = 13
	LMX_REG.REG6.REG6_bits.OUT_TERM = 12;				//OUTTERM = 13
	LMX_REG.REG6.REG6_bits.DIV_GAIN = 2;				//DIVGAIN = 13

	LMX_REG.REG5.DWORD_REG5 = 0xA0000000;
	LMX_REG.REG5.REG5_bits.address = 5 ;
	LMX_REG.REG5.REG5_bits.FL_TOC = 0 ;					//Time Out Counter for FastLock
	LMX_REG.REG5.REG5_bits.FL_CPG = 0 ;					//Charge Pump Current for Fastlock (3.2 mA)
	LMX_REG.REG5.REG5_bits.FL_R3_LF = 1 ;				//K = sqrt (FL_CPG/CPG) , FL_R3_LF = R3_LF / K
	LMX_REG.REG5.REG5_bits.FL_R4_LF = 0 ;				//FL_R4_LF = R4_LF/K

	LMX_REG.REG4.DWORD_REG4 = 0;
	LMX_REG.REG4.REG4_bits.address = 4 ;
	LMX_REG.REG4.REG4_bits.OSC_FREQ = LMX_OSC_FREQ ;
	LMX_REG.REG4.REG4_bits.VCO_DIV = 2 ;
	LMX_REG.REG4.REG4_bits.R3_LF = 1 ;					// 1 Kohm
	LMX_REG.REG4.REG4_bits.R4_LF = 0 ;					// 200 ohm
	LMX_REG.REG4.REG4_bits.C3_LF = 8 ;					// 20 pf
	LMX_REG.REG4.REG4_bits.C4_LF = 8 ;					// 100 pf

	LMX_REG.REG3.DWORD_REG3 = 0;
	LMX_REG.REG3.REG3_bits.address = 3 ;
	LMX_REG.REG3.REG3_bits.MODE = 0 ;		//Full
	LMX_REG.REG3.REG3_bits.POWER_DOWN = 0 ;	//POWERDOWN disabled
	LMX_REG.REG3.REG3_bits.XO = 0 ;			//External OSC Connected
	LMX_REG.REG3.REG3_bits.CPG = 31 ;		//Charge Pump Current Gain 32 mA
	LMX_REG.REG3.REG3_bits.MUX = 3 ;		//MUX out disabled
	LMX_REG.REG3.REG3_bits.CPP = 0 ;		//Charge Pump Polarity (Negative)(Full Chip Mode)
	LMX_REG.REG3.REG3_bits.OSC_2X = LMX_OSC_2X ;
	LMX_REG.REG3.REG3_bits.FDM = 1 ;		//Fractional divider 22 bit
	LMX_REG.REG3.REG3_bits.ORDER = 4 ;		//Traditional Delta Sigma Operation
	LMX_REG.REG3.REG3_bits.DITH = 3 ;		//Dithering disable
	LMX_REG.REG3.REG3_bits.CPT = 0 ;		//Charge Pump TRI-STATE (Normal Operation)
	LMX_REG.REG3.REG3_bits.DLOCK = 0 ;		//Controls for Digital Lock detect (13.5)
	LMX_REG.REG3.REG3_bits.FSK = 0 ;		//FSK disabled

	//N = PLL_N + PLL_NUM / PLL_DEN			fVCO = fPD * N = fOSCin * N / R

	LMX_REG.REG2.DWORD_REG2 = 0x04000000;
	LMX_REG.REG2.REG2_bits.address = 2;
	LMX_REG.REG2.REG2_bits.DEN = LMX_VCO_DEN ;

	LMX_REG.REG1.DWORD_REG1 = 0;
	LMX_REG.REG1.REG1_bits.address = 1;
	LMX_REG.REG1.REG1_bits.PLL_NUM = LMX_NUM >> 16 ;
	LMX_REG.REG1.REG1_bits.PLL_N = 0 ;
	LMX_REG.REG1.REG1_bits.PLL_R = LMX_R_COUNTER ;

	LMX_REG.REG0.DWORD_REG0 = 0;
	LMX_REG.REG0.REG0_bits.address = 0;
	LMX_REG.REG0.REG0_bits.PLL_NUM = LMX_NUM & 0xFFFF;
	LMX_REG.REG0.REG0_bits.PLL_N = LMX_PLL_N ;

	LMX2541_Write((unsigned char*)&LMX_REG , sizeof(LMX_REG) , LMX2541_DATA_LENGTH_BITS_32);
	if(ConfigRoot.G_Wireless.SG_Uhf.param_carrier_freq_number == 0)
		return LMX2541_Set_Chanel_Freq(); //UCHAN setting
	else
	{	//CHAN setting
		if(ConfigRoot.G_Wireless.SG_Uhf.param_carrier_freq_number > 8800)
			ConfigRoot.G_Wireless.SG_Uhf.param_carrier_freq_number = 1;
		LMX2541_Set_Frequency((ConfigRoot.G_Wireless.SG_Uhf.param_carrier_freq_number * 6250 + 415000000)*2);
	}
	return 0;
}

int LMX2541_Set_Chanel_Freq(void)
{
	LMX2541_Frequency = Current_Channel.freq;
	if((LMX2541_Frequency < 415006250) || (LMX2541_Frequency > 470000000))
		return -1 ;
	return LMX2541_Set_Frequency(LMX2541_Frequency*2);
}

unsigned int LMX2541_Lock_Detect(void)
{
	if(gpio_get_pin_value(LMX2541_LE_PIN))
		return 1;
	else
		return 0;
}

int LMX2541_Set_Frequency(U64 F_Out_in_hz)
{

	if(F_Out_in_hz < 832012500 && F_Out_in_hz > 831987500)
		F_Out_in_hz = 832012500 ;
	if(F_Out_in_hz < 864012500 && F_Out_in_hz > 863987500)
		F_Out_in_hz = 864012500 ;
	if(F_Out_in_hz < 886012500 && F_Out_in_hz > 885987500)
		F_Out_in_hz = 886012500 ;
	if(F_Out_in_hz < 928012500 && F_Out_in_hz > 927987500)
		F_Out_in_hz = 928012500 ;


	U64 F_Out_N ;
	U64 PLL_N ;
	U64 PLL_NUM ;
	U64 VCO_DIV = 1;
	Timer_Tik LMX_tik;
	struct CbtbFunctional New_CbTb_func ;

	CBTB_OID_FUNCTIONAL(&New_CbTb_func);
	LMX_Freq_Error = New_CbTb_func.FreqErr ;
	LMX_Freq_Error *= F_Out_in_hz ;
	if(New_CbTb_func.Freq)
		LMX_Freq_Error /= New_CbTb_func.Freq;
	else
		LMX_Freq_Error = 0;
	F_Out_in_hz -= LMX_Freq_Error ;

	if(F_Out_in_hz==0)
		return -1;

	while((F_Out_in_hz*VCO_DIV) < LMX_VCO_LOWER_LIMIT)
		VCO_DIV++ ;

	if((F_Out_in_hz*VCO_DIV) > LMX_VCO_UPPER_LIMIT)
		return -1 ;

	PLL_N = ((F_Out_in_hz*LMX_R_COUNTER*VCO_DIV)/DOUBLER_ON)/LMX_FOSC;
	F_Out_N = (PLL_N*LMX_FOSC*DOUBLER_ON)/(LMX_R_COUNTER*VCO_DIV) ;
	PLL_NUM = (((F_Out_in_hz - F_Out_N)*(LMX_VCO_DEN *LMX_R_COUNTER*VCO_DIV)/DOUBLER_ON) / LMX_FOSC);

	LMX_REG.REG4.REG4_bits.VCO_DIV = VCO_DIV ;

	LMX_REG.REG1.DWORD_REG1 = 0;
	LMX_REG.REG1.REG1_bits.address = 1;
	LMX_REG.REG1.REG1_bits.PLL_NUM = (PLL_NUM >> 16) & 0x3F;
	LMX_REG.REG1.REG1_bits.PLL_N = (PLL_N >> 12) & 0x3F  ;
	LMX_REG.REG1.REG1_bits.PLL_R = LMX_R_COUNTER ;

	LMX_REG.REG0.DWORD_REG0 = 0;
	LMX_REG.REG0.REG0_bits.address = 0;
	LMX_REG.REG0.REG0_bits.PLL_NUM = PLL_NUM & 0xFFFF;
	LMX_REG.REG0.REG0_bits.PLL_N = PLL_N & 0xFFF;

	LMX2541_Write((unsigned char*)&LMX_REG.REG4.DWORD_REG4 , 4 , LMX2541_DATA_LENGTH_BITS_32);
	LMX2541_Write((unsigned char*)&LMX_REG.REG1.DWORD_REG1 , 4 , LMX2541_DATA_LENGTH_BITS_32);
	LMX2541_Write((unsigned char*)&LMX_REG.REG0.DWORD_REG0 , 4 , LMX2541_DATA_LENGTH_BITS_32);

	LMX2541_Frequency = F_Out_in_hz/2;
	LMX_tik = TC1_Get_us();
	while(!LMX2541_In_lock() && (TC1_Get_us()-LMX_tik) < 5000);
	return 1 ;
}

void LMX2541_Sleep_Wake(unsigned char state )
{
	if(state == 0)
		LMX_REG.REG3.REG3_bits.POWER_DOWN = 1 ;	//POWERDOWN Enable
	else
		LMX_REG.REG3.REG3_bits.POWER_DOWN = 0 ;	//POWERDOWN disabled
	LMX2541_Write((unsigned char*)&LMX_REG.REG3.DWORD_REG3 , 4 , LMX2541_DATA_LENGTH_BITS_32);
}

int Find_Frequency_Band(unsigned int freq_hz , unsigned int* multiplier)
{
	unsigned int Min_freq = 415000000;
	unsigned int Max_freq = 470006250;
	unsigned char Freq_band_count ;
	unsigned int delta_freq ;
	unsigned int i ;

	CBTB_OID_NUMBER_OF_FREQUENCY_SUB_BANDS(&Freq_band_count);
	if(Freq_band_count > 16 || Freq_band_count < 2)
		Freq_band_count = 6 ;
	delta_freq = (Max_freq - Min_freq)/(Freq_band_count-1);
	if(freq_hz < Min_freq || freq_hz > Max_freq)
		return -1 ;
	for(i = 1 ; i < Freq_band_count ; i++)
	{
		if(freq_hz <= Min_freq + i*delta_freq)
			break ;
	}
	*multiplier = ((freq_hz - (Min_freq + (i-1)*delta_freq))*10)/delta_freq ;
	return i ;
}

int LMX2541_Freq_Error(void)
{
	return (LMX2541_Frequency - Current_Channel.freq);
}



